package com.sojson.util.file.system.impl;

import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;

import com.sojson.constant.Constant;
import com.sojson.util.ExpUtil;
import com.sojson.util.StringUtil;
import com.sojson.util.file.FileUtil;
import com.sojson.util.file.bean.UploadBean;
import com.sojson.util.file.system.FileSystemInterface;
import com.sojson.util.file.system.impl.minio.config.MinioConfig;
import com.sojson.util.id.IdUtil;
import com.sojson.util.init.InitUtil;
import com.sojson.util.text.Convert;

import lombok.extern.slf4j.Slf4j;

/**
  * Ftp文件系统工具类
  *
  * @author liu
  * @date 2022-02-24
  */
@Slf4j
public class FtpUtilImpl implements FileSystemInterface {

    private static FtpUtilImpl ftpUtilImpl;
    private FTPClient client = getClient();
    /** Ftp服务器的地址 */
    private final String URL = Constant.PROPERTIES.getProperty("so.ftp.url");
    /** Ftp服务器的端口号 */
    private final int PORT = Convert.toInt(Constant.PROPERTIES.getProperty("so.ftp.port"), 21);
    /** Ftp服务器的帐号 */
    private final String USERNAME = Constant.PROPERTIES.getProperty("so.ftp.username");
    /** Ftp服务器的密码 */
    private final String PWD = Constant.PROPERTIES.getProperty("so.ftp.pwd");

    private FtpUtilImpl() {}

    @Override
    protected void finalize() throws Throwable {
        close();
    }

    /**
     * 文件上传
     *
     * @param filePath  文件位置
     * @param dir       上传目录
     * @return
     * @throws Exception
     */
    @Override
    public UploadBean upload(String filePath, String dir) throws Exception {
        InputStream inputStream = null;
        try {
            inputStream = FileUtil.readInputStream(filePath);
            return upload(inputStream, dir, FileUtil.getFileSuffix(filePath));
        } finally {
            FileUtil.closeStream(inputStream);
        }
    }

    /**
     * 文件上传
     *
     * @param fileStream    文件流
     * @param dir           上传目录
     * @param fileSuffix    文件后缀
     * @return
     * @throws Exception
     */
    @Override
    public UploadBean upload(InputStream fileStream, String dir, String fileSuffix) throws Exception {
        dir = "/" + dir;
        String fileName = IdUtil.uuid() + fileSuffix;
        String objectName = dir + "/" + fileName;

        // 创建目录
        mkdir(BUCKET_NAME + dir);

        // 上传文件
        boolean storeFile = client.storeFile(fileName, fileStream);
        if (!storeFile) {
            ExpUtil.throwEx("上传文件失败");
        }

        String packageKey = getUrl(objectName);

        UploadBean upload = new UploadBean();
        upload.setObjectName(packageKey);
        return upload;
    }

    /**
     * 文件上传
     *
     * @param fileBytes     文件字节流
     * @param dir           上传目录
     * @param fileSuffix    文件后缀
     * @return
     * @throws Exception
     */
    @Override
    public UploadBean upload(byte[] fileBytes, String dir, String fileSuffix) throws Exception {
        InputStream inputStream = null;
        try {
            inputStream = FileUtil.readInputStream(fileBytes);
            return upload(inputStream, dir, fileSuffix);
        } finally {
            FileUtil.closeStream(inputStream);
        }
    }

    /**
     * 下载文件
     *
     * @param objectName    唯一标识
     * @param dir           文件路径
     * @param fileName      文件名
     * @return
     * @throws Exception
     */
    @Override
    public boolean download(String objectName, String dir, String fileName) throws Exception {
        InputStream inputStream = null;
        try {
            inputStream = downloadGetStream(objectName);
            FileUtil.write(inputStream, new StringBuilder(FileUtil.THIS_LOCALITY_DIRECTORY_BUCKET_NAME_BASE).append("/")
                .append(dir).append("/").append(fileName).toString());
        } finally {
            FileUtil.closeStream(inputStream);
        }
        return true;
    }

    /**
     * 下载文件
     *
     * @param objectName    唯一标识
     * @return
     * @throws Exception
     */
    @Override
    public InputStream downloadGetStream(String objectName) throws Exception {
        return client.retrieveFileStream(BUCKET_NAME + objectName);
    }

    /**
     * 下载文件
     *
     * @param objectName    唯一标识
     * @return
     * @throws Exception
     */
    @Override
    public byte[] downloadGetBytes(String objectName) throws Exception {
        InputStream inputStream = null;
        try {
            inputStream = downloadGetStream(objectName);
            return FileUtil.readByes(inputStream);
        } finally {
            FileUtil.closeStream(inputStream);
        }
    }

    /**
     * 删除文件
     *
     * @param objectName    唯一标识
     * @return
     * @throws Exception
     */
    @Override
    public boolean remove(String objectName) throws Exception {
        return client.deleteFile(BUCKET_NAME + objectName);
    }

    /**
     * 获取文件路径
     *
     * @param objectName    唯一标识
     * @return
     * @throws Exception
     */
    @Override
    public String getUrl(String objectName) throws Exception {
        StringBuilder sb = new StringBuilder(MinioConfig.ENDPOINT);
        sb.append("/");
        sb.append(BUCKET_NAME);
        sb.append("/");
        sb.append(objectName);
        return BUCKET_NAME + objectName;
    }

    /**
     * 获取文件路径(临时路径)
     *
     * @param objectName    唯一标识
     * @return
     * @throws Exception
     */
    @Override
    public String getUrlTemporary(String objectName) throws Exception {
        return null;
    }

    /**
     * 获取文件路径(临时路径)
     *
     * @param objectName    唯一标识
     * @param timeout       超时时间(单位: 秒)
     * @return
     * @throws Exception
     */
    @Override
    public String getUrlTemporary(String objectName, int timeout) throws Exception {
        return null;
    }

    /**
     * 创建目录
     *
     * @param filePath
     * @return
     * @throws IOException
     */
    private boolean mkdir(String filePath) throws IOException {
        // 切换到上传目录并返回成功还是失败
        if (client.changeWorkingDirectory(filePath)) {
            return true;
        }

        filePath = filePath.replace("\\", "/");
        // 如果目录不存在创建目录
        String[] dirs = filePath.split("/");
        String tempPath = "";
        for (String dir : dirs) {
            if (StringUtil.isBlank(dir)) {
                continue;
            }

            tempPath = tempPath + "/" + dir;
            if (!client.changeWorkingDirectory(tempPath)) {
                if (!client.makeDirectory(tempPath)) {
                    ExpUtil.throwEx("Ftp服务器创建目录失败");
                } else {
                    client.changeWorkingDirectory(tempPath);
                }
            }
        }
        return true;
    }

    /**
     * 获取客户端连接
     *
     * @return
     */
    private FTPClient getClient() {
        FTPClient ftp = new FTPClient();
        // 连接FTP服务器
        // 如果采用默认端口，可以使用ftp.connect(host)的方式直接连接FTP服务器
        try {
            ftp.connect(URL, PORT);

            // 登录
            if (StringUtil.isNotBlank(USERNAME)) {
                ftp.login(USERNAME, PWD);
            }
            int reply = ftp.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftp.disconnect();
                ExpUtil.throwEx("Ftp服务器连接异常");
                return null;
            }
            // 设置上传文件的类型为二进制类型
            ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
            if (URL != null) {
                InitUtil.add("当前文件存储系统FTP的IP为: " + URL);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return ftp;
    }

    private void close() {
        log.info("正在关闭FTP");
        try {
            if (client == null) {
                return;
            }

            // 退出登录
            client.logout();
        } catch (Exception e) {
            log.error("Ftp服务器关闭异常," + e.getMessage());
        } finally {
            if (client.isConnected()) {
                try {
                    client.disconnect();
                } catch (IOException ioe) {
                    log.error("Ftp服务器关闭异常," + ioe.getMessage());
                }
            }
        }
    }

    public static FtpUtilImpl getInstances() {
        if (StringUtil.isBlankObject(ftpUtilImpl)) {
            ftpUtilImpl = new FtpUtilImpl();
        }
        return ftpUtilImpl;
    }

}